#include "Build.h"

#include "..\EnumMapping.h"
#include "..\Util.h"

namespace BroodWar
{
	namespace Addon
	{
		Build^ ConvertBuild(BWSAL::BuildType* build)
		{
			if(build == NULL)
				return nullptr;
			return gcnew Build(build);
		}

		Build^ ConvertBuild(BWSAL::BuildType build)
		{
			return gcnew Build(build);
		}

		BWSAL::BuildType ConvertBuild(Build^ build)
		{
			if(Object::ReferenceEquals(build, nullptr))
				return BWSAL::BuildType();
			return BWSAL::BuildType(*(build->instance));
		}

		BWSAL::BuildType ConvertBuild(BuildType build)
		{
			return EnumMapping::BuildType->Native(build);
		}

		Build::Build(BWSAL::BuildType* type)
		{
			instance = type;
			dispose = false;
		}

		Build::Build(BWSAL::BuildType type)
		{
			instance = new BWSAL::BuildType(type);
			dispose = true;
		}

		Build::~Build()
		{
			if(dispose)
				delete instance;
		}

		Build::!Build()
		{
			if(dispose)
				delete instance;
		}

		Build::Build()
		{
			instance = new BWSAL::BuildType();
			dispose = true;
		}
		
		Build::Build(int id)
		{
			instance = new BWSAL::BuildType(id);
			dispose = true;
		}
		
		Build::Build(Build^ other)
		{
			instance = new BWSAL::BuildType(ConvertBuild(other));
			dispose = true;
		}
		
		Build::Build(Api::Tech^ other)
		{
			instance = new BWSAL::BuildType(ConvertTech(other));
			dispose = true;
		}
		
		Build::Build(Api::UnitType^ other)
		{
			instance = new BWSAL::BuildType(ConvertUnitType(other));
			dispose = true;
		}
		
		Build::Build(Api::Enum::UnitType other)
		{
			instance = new BWSAL::BuildType(ConvertUnitType(other));
			dispose = true;
		}
		
		Build::Build(Api::Upgrade^ other)
		{
			instance = new BWSAL::BuildType(ConvertUpgrade(other));
			dispose = true;
		}
		
		Build::Build(Api::Upgrade^ other, int level)
		{
			instance = new BWSAL::BuildType(ConvertUpgrade(other), level);
			dispose = true;
		}
		
		Build::Build(Api::Enum::UpgradeType other)
		{
			instance = new BWSAL::BuildType(ConvertUpgrade(other));
			dispose = true;
		}
		
		Build::Build(Api::Enum::UpgradeType other, int level)
		{
			instance = new BWSAL::BuildType(ConvertUpgrade(other), level);
			dispose = true;
		}

		int Build::Id::get()
		{
			return instance->getID();
		}
		
		BuildType Build::Type::get()
		{
			return EnumMapping::BuildType->Managed(instance);
		}
		
		bool Build::TypeEquals(BuildType buildType)
		{
			return Type == buildType;
		}
		
		Api::Race^ Build::Race::get()
		{
			return ConvertRace(instance->getRace());
		}

		bool Build::IsTechType::get()
		{
			return instance->isTechType();
		}
		
		bool Build::IsUnitType::get()
		{
			return instance->isUnitType();
		}
		
		bool Build::IsUpgradeType::get()
		{
			return instance->isUpgradeType();
		}
		
		Api::Tech^ Build::Tech::get()
		{
			return ConvertTech(instance->getTechType());
		}
		
		Api::UnitType^ Build::UnitType::get()
		{
			return ConvertUnitType(instance->getUnitType());
		}
		
		Api::Upgrade^ Build::Upgrade::get()
		{
			return ConvertUpgrade(instance->getUpgradeType());
		}
		
		int Build::UpgradeLevel::get()
		{
			return instance->getUpgradeLevel();
		}
		
		unsigned int Build::Mask::get()
		{
			return instance->getMask();
		}
		
		unsigned int Build::RequiredMask::get()
		{
			return instance->getRequiredMask();
		}

		Tuple<Build^, int>^ Build::WhatBuilds::get()
		{
			std::pair<BWSAL::BuildType, int> wb = instance->whatBuilds();
			return gcnew Tuple<Build^, int>(gcnew Build(wb.first), wb.second);
		}
		
		Dictionary<Build^, int>^ Build::RequiredBuildTypes::get()
		{
			std::map<BWSAL::BuildType, int> rb = instance->requiredBuildTypes();
			Dictionary<Build^, int>^ result = gcnew Dictionary<Build^, int>(rb.size());
			std::map<BWSAL::BuildType, int>::iterator it;
			for(it = rb.begin(); it != rb.end(); it++)
				result->Add(gcnew Build(it->first), it->second);
			return result;
		}

		bool Build::RequiresPsi::get()
		{
			return instance->requiresPsi();
		}

		bool Build::RequiresLarva::get()
		{
			return instance->requiresLarva();
		}

		Build^ Build::RequiredAddon::get()
		{
			return gcnew Build(instance->requiredAddon());
		}

		int Build::MineralPrice::get()
		{
			return instance->mineralPrice();
		}

		int Build::GasPrice::get()
		{
			return instance->gasPrice();
		}

		int Build::BuilderTime::get()
		{
			return instance->builderTime();
		}

		int Build::BuildUnitTime::get()
		{
			return instance->buildUnitTime();
		}

		int Build::PrepTime::get()
		{
			return instance->prepTime();
		}

		bool Build::CreatesUnit::get()
		{
			return instance->createsUnit();
		}

		bool Build::MorphsBuilder::get()
		{
			return instance->morphsBuilder();
		}

		bool Build::NeedsBuildLocation::get()
		{
			return instance->needsBuildLocation();
		}

		int Build::SupplyRequired::get()
		{
			return instance->supplyRequired();
		}

		int Build::SupplyProvided::get()
		{
			return instance->supplyProvided();
		}

		bool Build::DoBuild(Unit^ builder, Unit^ secondBuilder, TilePosition^ buildLocation)
		{
			return instance->build(ConvertUnit(builder), ConvertUnit(secondBuilder), ConvertTile(buildLocation));
		}
		
		bool Build::IsPreparing(Unit^ builder, Unit^ secondBuilder)
		{
			return instance->isPreparing(ConvertUnit(builder), ConvertUnit(secondBuilder));
		}
		
		bool Build::IsBuilding(Unit^ builder, Unit^ secondBuilder, Unit^ createdUnit)
		{
			return instance->isBuilding(ConvertUnit(builder), ConvertUnit(secondBuilder), ConvertUnit(createdUnit));
		}
		
		bool Build::IsCompleted(Unit^ builder, Unit^ secondBuilder, Unit^ createdUnit, Unit^ secondCreatedUnit)
		{
			return instance->isCompleted(ConvertUnit(builder), ConvertUnit(secondBuilder), ConvertUnit(createdUnit), ConvertUnit(secondCreatedUnit));
		}
		
		int Build::RemainingTime(Unit^ builder, Unit^ secondBuilder, Unit^ createdUnit)
		{
			return instance->remainingTime(ConvertUnit(builder), ConvertUnit(secondBuilder), ConvertUnit(createdUnit));
		}

		int Build::GetHashCode()
		{
			return instance->getID();
		}

		bool Build::Equals(Object^ o)
		{
			Build^ other = dynamic_cast<Build^>(o);
			return this->Equals(other);
		}
		
		String^ Build::ToString()
		{
			return gcnew String(instance->getName().c_str());
		}
		
		bool Build::Equals(Build^ other)
		{
			if(ReferenceEquals(nullptr, other))
				return false;
			if(ReferenceEquals(this, other))
				return true;
			return this->instance->getID() == other->instance->getID();
		}

		HashSet<Build^>^ Build::AllBuildTypes()
		{
			return ToHashSet<BWSAL::BuildType, Build^>(BWSAL::BuildTypes::allBuildTypes(), &ConvertBuild);
		}

		HashSet<Build^>^ Build::AllBuildTypes(Api::Race^ race)	
		{
			return ToHashSet<BWSAL::BuildType, Build^>(BWSAL::BuildTypes::allBuildTypes(ConvertRace(race)), &ConvertBuild);
		}

		HashSet<Build^>^ Build::AllRequiredBuildTypes()
		{
			return ToHashSet<BWSAL::BuildType, Build^>(BWSAL::BuildTypes::allRequiredBuildTypes(), &ConvertBuild);
		}

		HashSet<Build^>^ Build::AllRequiredBuildTypes(Api::Race^ race)	
		{
			return ToHashSet<BWSAL::BuildType, Build^>(BWSAL::BuildTypes::allRequiredBuildTypes(ConvertRace(race)), &ConvertBuild);
		}

		unsigned int Build::WorkerMask::get()
		{
			return BWSAL::BuildTypes::WorkerMask;
		}
		
		unsigned int Build::RefineryMask::get()
		{
			return BWSAL::BuildTypes::RefineryMask;
		}

		unsigned int Build::SupplyMask::get()
		{
			return BWSAL::BuildTypes::SupplyMask;
		}

		unsigned int Build::CenterMask::get()
		{
			return BWSAL::BuildTypes::CenterMask;
		}

		bool Build::operator == (Build^ first, Build^ second)
		{
			if(ReferenceEquals(first, second))
				return true;
			if(ReferenceEquals(nullptr, first))
				return false;
			return first->Equals(second);
		}

		bool Build::operator != (Build^ first, Build^ second)
		{
			return !(first == second);
		}
		
		bool Build::operator == (Build^ first, Api::UnitType^ second)
		{
			if(ReferenceEquals(first, nullptr) || ReferenceEquals(second, nullptr))
				return false;
			return ConvertBuild(first) == ConvertUnitType(second);
		}
		
		bool Build::operator == (Build^ first, Api::Enum::UnitType second)
		{
			if(ReferenceEquals(first, nullptr) || ReferenceEquals(second, nullptr))
				return false;
			return ConvertBuild(first) == ConvertUnitType(second);
		}
		
		bool Build::operator == (Build^ first, Api::Tech^ second)
		{
			if(ReferenceEquals(first, nullptr) || ReferenceEquals(second, nullptr))
				return false;
			return ConvertBuild(first) == ConvertTech(second);
		}
		
		bool Build::operator == (Build^ first, Api::Upgrade^ second)
		{
			if(ReferenceEquals(first, nullptr) || ReferenceEquals(second, nullptr))
				return false;
			return ConvertBuild(first) == ConvertUpgrade(second);
		}
		
		bool Build::operator == (Build^ first, UpgradeType second)
		{
			if(ReferenceEquals(first, nullptr) || ReferenceEquals(second, nullptr))
				return false;
			return ConvertBuild(first) == ConvertUpgrade(second);
		}
		
		bool Build::operator < (Build^ first, Build^ second)
		{
			if(ReferenceEquals(first, second))
				return false;
			if(ReferenceEquals(first, nullptr) || ReferenceEquals(second, nullptr))
				return false;
			return ConvertBuild(first) < ConvertBuild(second);
		}
	}
}
